package com.hk.core.authentication.api.reactive;

import com.hk.commons.util.SpringContextHolder;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @author Kevin
 * @date 2024-03-24 19:43
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ReactiveUserPrincipalInvoke {

    /**
     * 如果有指定的角色，从Spring 容器中获取对应的Bean,调用 mapper方法返回数据
     *
     * @param role            用户是否有指定角色
     * @param operatorService 调用的Bean 类型
     * @param noMatchSupplier 无有指定角色，或从Spring容器中找不到对应的Bean时的默认 Service
     * @param mapper          调用的方法
     */
    public static <S, R> Mono<R> invokeByRoleMono(String role, Class<S> operatorService,
                                                  Supplier<S> noMatchSupplier, Function<S, Mono<R>> mapper) {
        return ReactiveSecurityContextUtils.hasRole(role)
                .flatMap(hasRole -> {
                    S service = hasRole ? SpringContextHolder.getBeanIfExist(operatorService).orElseGet(noMatchSupplier) : noMatchSupplier.get();
                    return mapper.apply(service);
                });
    }

    /**
     * 如果有指定的角色，从Spring 容器中获取对应的Bean,调用 mapper方法返回数据
     *
     * @param role            用户是否有指定角色
     * @param operatorService 调用的Bean 类型
     * @param noMatchSupplier 无有指定角色，或从Spring容器中找不到对应的Bean时的默认 Service
     * @param mapper          调用的方法
     */
    public static <S, R> Flux<R> invokeByRoleFlux(String role, Class<S> operatorService,
                                                  Supplier<S> noMatchSupplier, Function<S, Flux<R>> mapper) {
        return ReactiveSecurityContextUtils.hasRole(role)
                .flatMapMany(hasRole -> {
                    S service = hasRole ? SpringContextHolder.getBeanIfExist(operatorService).orElseGet(noMatchSupplier) : noMatchSupplier.get();
                    return mapper.apply(service);
                });
    }

    public static <S, R> Mono<R> invokeByRole(String role, S operatorService,
                                              Supplier<S> noMatchSupplier, Function<S, Mono<R>> mapper) {
        return ReactiveSecurityContextUtils.hasRole(role)
                .flatMap(hasRole -> hasRole ? mapper.apply(operatorService) : mapper.apply(noMatchSupplier.get()));
    }

    public static <S, R> Flux<R> invokeByRoleFlux(String role, S operatorService,
                                                  Supplier<S> noMatchSupplier, Function<S, Flux<R>> mapper) {
        return ReactiveSecurityContextUtils.hasRole(role)
                .flatMapMany(hasRole -> hasRole ? mapper.apply(operatorService) : mapper.apply(noMatchSupplier.get()));
    }

    public static <S, R> Mono<R> invokeByRoles(String[] roles, S operatorService,
                                               Supplier<S> noMatchSupplier, Function<S, Mono<R>> mapper) {
        return ReactiveSecurityContextUtils.hasAnyRole(roles)
                .flatMap(hasRole -> hasRole ? mapper.apply(operatorService) : mapper.apply(noMatchSupplier.get()));
    }

    public static <S, R> Flux<R> invokeByRolesFlux(String[] roles, S operatorService,
                                                   Supplier<S> noMatchSupplier, Function<S, Flux<R>> mapper) {
        return ReactiveSecurityContextUtils.hasAnyRole(roles)
                .flatMapMany(hasRole -> hasRole ? mapper.apply(operatorService) : mapper.apply(noMatchSupplier.get()));
    }

    public static <S, R> Mono<R> invokeByRolesMono(String[] roles, Class<S> operatorService,
                                                   Supplier<S> noMatchSupplier, Function<S, Mono<R>> mapper) {
        return ReactiveSecurityContextUtils.hasAnyRole(roles)
                .flatMap(hasRole -> {
                    S service = hasRole ? SpringContextHolder.getBeanIfExist(operatorService).orElseGet(noMatchSupplier) : noMatchSupplier.get();
                    return mapper.apply(service);
                });
    }

    public static <S, R> Flux<R> invokeByRolesFlux(String[] roles, Class<S> operatorService,
                                                   Supplier<S> noMatchSupplier, Function<S, Flux<R>> mapper) {
        return ReactiveSecurityContextUtils.hasAnyRole(roles)
                .flatMapMany(hasRole -> {
                    S service = hasRole ? SpringContextHolder.getBeanIfExist(operatorService).orElseGet(noMatchSupplier) : noMatchSupplier.get();
                    return mapper.apply(service);
                });
    }

    public static <S, R> Mono<R> invokeByPermissionMono(String permission, Class<S> operatorService,
                                                        Supplier<S> noMatchSupplier, Function<S, Mono<R>> mapper) {
        return ReactiveSecurityContextUtils.hasPermission(permission)
                .flatMap(hasRole -> {
                    S service = hasRole ? SpringContextHolder.getBeanIfExist(operatorService).orElseGet(noMatchSupplier) : noMatchSupplier.get();
                    return mapper.apply(service);
                });
    }

    public static <S, R> Flux<R> invokeByPermissionFlux(String permission, Class<S> operatorService,
                                                        Supplier<S> noMatchSupplier, Function<S, Flux<R>> mapper) {
        return ReactiveSecurityContextUtils.hasPermission(permission)
                .flatMapMany(hasRole -> {
                    S service = hasRole ? SpringContextHolder.getBeanIfExist(operatorService).orElseGet(noMatchSupplier) : noMatchSupplier.get();
                    return mapper.apply(service);
                });
    }

    public static <S, R> Mono<R> invokeByPermissionMono(String permission, S operatorService,
                                                        Supplier<S> noMatchSupplier, Function<S, Mono<R>> mapper) {
        return ReactiveSecurityContextUtils.hasPermission(permission)
                .flatMap(hasRole -> hasRole ? mapper.apply(operatorService) : mapper.apply(noMatchSupplier.get()));
    }

    public static <S, R> Flux<R> invokeByPermissionFlux(String permission, S operatorService,
                                                        Supplier<S> noMatchSupplier, Function<S, Flux<R>> mapper) {
        return ReactiveSecurityContextUtils.hasPermission(permission)
                .flatMapMany(hasRole -> hasRole ? mapper.apply(operatorService) : mapper.apply(noMatchSupplier.get()));
    }

    public static <S, R> Mono<R> invokeByPermissionsMono(String[] permissions, Class<S> operatorService,
                                                         Supplier<S> noMatchSupplier, Function<S, Mono<R>> mapper) {
        return ReactiveSecurityContextUtils.hasAnyPermission(permissions)
                .flatMap(hasRole -> {
                    S service = hasRole ? SpringContextHolder.getBeanIfExist(operatorService).orElseGet(noMatchSupplier) : noMatchSupplier.get();
                    return mapper.apply(service);
                });
    }

    public static <S, R> Flux<R> invokeByPermissionsFlux(String[] permissions, Class<S> operatorService,
                                                         Supplier<S> noMatchSupplier, Function<S, Flux<R>> mapper) {
        return ReactiveSecurityContextUtils.hasAnyPermission(permissions)
                .flatMapMany(hasRole -> {
                    S service = hasRole ? SpringContextHolder.getBeanIfExist(operatorService).orElseGet(noMatchSupplier) : noMatchSupplier.get();
                    return mapper.apply(service);
                });
    }

    public static <S, R> Mono<R> invokeByPermissionsMono(String[] permissions, S operatorService,
                                                         Supplier<S> noMatchSupplier, Function<S, Mono<R>> mapper) {
        return ReactiveSecurityContextUtils.hasAnyPermission(permissions)
                .flatMap(hasRole -> hasRole ? mapper.apply(operatorService) : mapper.apply(noMatchSupplier.get()));
    }

    public static <S, R> Flux<R> invokeByPermissionsFlux(String[] permissions, S operatorService,
                                                         Supplier<S> noMatchSupplier, Function<S, Flux<R>> mapper) {
        return ReactiveSecurityContextUtils.hasAnyPermission(permissions)
                .flatMapMany(hasRole -> hasRole ? mapper.apply(operatorService) : mapper.apply(noMatchSupplier.get()));
    }
}
